import { JsonOb } from './JsonOb';

const VM_EMIT_HEAD = 'VC:';
const DEBUG_SHOW_PATH = false;


//通过 .  路径 设置值
function setValueFromPath(obj:any,path: string, value: any, tag:string = '') {
    let props = path.split('.');
    for (let i = 0; i < props.length; i++) {
        const propName = props[i];
        if (propName in obj === false) { console.error('['+propName + '] not find in ' +tag+'.'+ path); break; }
        if (i == props.length - 1) {
            obj[propName] = value;
        } else {
            obj = obj[propName];
        }
    }

}

//通过 . 路径 获取值
function getValueFromPath(obj:any,path: string,def?:any, tag:string = ''):any {
    let props = path.split('.');
    for (let i = 0; i < props.length; i++) {
        const propName = props[i];
        if ((propName in obj === false)) { console.error('['+propName + '] not find in '+tag+'.'+ path); return def; }
        obj = obj[propName];
    }
    if(obj === null||typeof obj === "undefined")obj = def;//如果g == null 则返回一个默认值
    return obj;

}



/**
 * ModelViewer 类
 */
class ViewModel<T>{
    constructor(data:T,tag:string) {
        new JsonOb(data,this._callback.bind(this));
        this.$data = data;
        this._tag = tag;
    }

    public $data:T;

    //索引值用的标签
    private _tag:string = null;

    /**激活状态, 将会通过 cc.director.emit 发送值变动的信号, 适合需要屏蔽的情况 */
    public active:boolean = true;

    /**是否激活根路径回调通知, 不激活的情况下 只能监听末端路径值来判断是否变化 */
    public emitToRootPath:boolean = false;

    //回调函数 请注意 回调的 path 数组是 引用类型，禁止修改
    private _callback(n: any, o: any, path:string[]):void{
            if(this.active == true){
                let name = VM_EMIT_HEAD + this._tag+'.'+ path.join('.')
                if(DEBUG_SHOW_PATH)cc.log('>>',n,o,path);
                cc.director.emit(name,n,o,[this._tag].concat(path)); //通知末端路径

                if(this.emitToRootPath)cc.director.emit(VM_EMIT_HEAD+this._tag,n,o,path);//通知主路径

                if(path.length>=2){
                    for (let i = 0; i < path.length-1; i++) {
                        const e = path[i];
                        //cc.log('中端路径');
                        
                    }
                }

            }
    }

    //通过路径设置数据的方法
    public setValue(path: string, value: any) {
        setValueFromPath(this.$data,path,value,this._tag);
    }
    //获取路径的值
    public getValue(path: string,def?:any):any {
        return getValueFromPath(this.$data,path,def,this._tag);
    }
}

/**
 * VM 对象管理器(工厂)
 */
class VMManager {
        /**静态数组，保存创建的 mv 组件 */
        private _mvs:Array<{tag:string,vm:ViewModel<any>}> = [];

        private EMIT_HEAD = VM_EMIT_HEAD;


        /**
         * 绑定一个数据，并且可以由VM所管理
         * @param data 需要绑定的数据
         * @param tag 对应该数据的标签(用于识别为哪个VM，不允许重复)
         * @param activeRootObject 激活主路径通知，可能会有性能影响，一般不使用
         */
        add<T>(data:T,tag:string = 'global',activeRootObject:boolean = false){
            let vm = new ViewModel<T>(data,tag);
            let has = this._mvs.find(v=>v.tag === tag);
            if(tag.includes('.')){
                console.error('cant write . in tag:',tag);
                return;
            }
            if(has){
                console.error('already set VM tag:'+ tag);
                return;
            }

            vm.emitToRootPath = activeRootObject;
            this._mvs.push({tag:tag,vm:vm});
            
        }

        /**
         * 移除并且销毁 VM 对象
         * @param tag 
         */
        remove(tag:string){
            let index = this._mvs.findIndex(v => v.tag === tag);
            if(index >=0 ) this._mvs.splice(index,1);
        }

        /**
         * 获取绑定的数据
         * @param tag 数据tag
         */
        get<T>(tag:string):ViewModel<T>{
            let res = this._mvs.find(v => v.tag === tag);
            if(res == null){
                console.error('cant find VM from:',tag);
            }else{
                return res.vm;
            }
        }

        /**
         * 通过全局路径,而不是 VM 对象来 设置值
         * @param path - 全局取值路径
         * @param value - 需要增加的值
         */
        addValue(path: string, value: any) {
            path =  path.trim();//防止空格,自动剔除
            let rs = path.split('.');
            if(rs.length<2){console.error('Cant find path:'+path)};
            let vm = this.get(rs[0]);
            if(!vm){console.error('Cant Set VM:'+rs[0]);return;};
            let resPath = rs.slice(1).join('.');
            vm.setValue(resPath,vm.getValue(resPath)+value);
        }

        /**
         * 通过全局路径,而不是 VM 对象来 获取值
         * @param path - 全局取值路径
         * @param def - 如果取不到值的返回的默认值
         */
        getValue(path: string,def?:any):any {
            path =  path.trim();//防止空格,自动剔除
            let rs = path.split('.');
            if(rs.length<2){console.error('Get Value Cant find path:'+path);return;};
            let vm = this.get(rs[0]);
            if(!vm){console.error('Cant Get VM:'+rs[0]);return;};
            return vm.getValue(rs.slice(1).join('.'),def);
        }

        /**
         * 通过全局路径,而不是 VM 对象来 设置值
         * @param path - 全局取值路径
         * @param value - 需要设置的值
         */
        setValue(path: string, value: any) {
            path =  path.trim();//防止空格,自动剔除
            let rs = path.split('.');
            if(rs.length<2){console.error('Set Value Cant find path:'+path);return;};
            let vm = this.get(rs[0]);
            if(!vm){console.error('Cant Set VM:'+rs[0]);return;};
            vm.setValue(rs.slice(1).join('.'),value);
  
        }

        setObjValue = setValueFromPath;
        getObjValue = getValueFromPath;

        /**等同于 cc.director.on */
        bindPath(path: string, callback: Function, target?: any, useCapture?: boolean):void{
            path =  path.trim();//防止空格,自动剔除
            if(path == ''){
                console.error(target.node.name,'节点绑定的路径为空');
                return;
            }
            if(path.split('.')[0] === '*'){
                console.error(path,'路径不合法,可能错误覆盖了 VMParent 的onLoad 方法, 或者父节点并未挂载 VMParent 相关的组件脚本');
                return;
            }
            cc.director.on(VM_EMIT_HEAD + path, callback, target, useCapture);
        }

        /**等同于 cc.director.off */
        unbindPath(path: string, callback: Function, target?: any):void{
            path =  path.trim();//防止空格,自动剔除
            if(path.split('.')[0] === '*'){
                console.error(path,'路径不合法,可能错误覆盖了 VMParent 的onLoad 方法, 或者父节点并未挂载 VMParent 相关的组件脚本');
                return;
            }
            cc.director.off(VM_EMIT_HEAD + path, callback, target);
        }


        /**冻结所有标签的 VM，视图将不会受到任何信息 */
        inactive():void{
            this._mvs.forEach(mv=>{
                mv.vm.active = false;
            })
        }

        /**激活所有标签的 VM*/
        active():void{
            this._mvs.forEach(mv=>{
                mv.vm.active = true;
            })
        }

 

}

//   整数、小数、时间、缩写

/**
 * VM管理对象,使用文档: 
 *  https://github.com/wsssheep/cocos_creator_mvvm_tools/blob/master/docs/ViewModelScript.md
 */
export let VM = new VMManager();